package org.hzero.interfaces.common.helper.impl;

import org.hzero.interfaces.common.entity.KnifeInterfaceLogRecord;
import org.hzero.interfaces.common.helper.KnifeInterfaceLogHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Map;

/**
 * AOP进行日志监控的实现方式
 * 2020-07-02
 * 注意本类的处理对非作用于Controller中的接口时,例如Service的实现类方法时,请求体会被合并到INTERFACE_REQ_URL_PARAM字段中;
 * 因为无法识别出本请求体参数究竟是{@link RequestBody}还是url上的参数,默认是未找见添加了{@link RequestBody}注解的参数就会被合并到INTERFACE_REQ_URL_PARAM字段
 *
 * @author: tianhao.luo@hand-china.com 2021/7/1  下午2:30
 */
public class KnifeAopInterfaceLogHelperImpl extends KnifeInterfaceLogHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeAopInterfaceLogHelperImpl.class);

    /**
     * aop进行日志处理时,需要外部告诉他hpms系统响应成功还是失败
     */
    private final HttpStatus httpStatus;

    public KnifeAopInterfaceLogHelperImpl(KnifeInterfaceLogRecord knifeInterfaceLogRecord, @NotNull HttpStatus httpStatus) {
        super(knifeInterfaceLogRecord);
        Assert.notNull(httpStatus, "不可以传入空的httpStatus");
        this.httpStatus = httpStatus;
    }

    public KnifeAopInterfaceLogHelperImpl(KnifeInterfaceLogRecord knifeInterfaceLogRecord, @NotNull HttpStatus httpStatus, Exception exception) {
        super(knifeInterfaceLogRecord, exception);
        Assert.notNull(httpStatus, "不可以传入空的httpStatus");
        this.httpStatus = httpStatus;
    }

    @Override
    public void preDeal(Method method) throws UnknownHostException {
        super.preDeal(method);
        // 记录日志中部分参数
        buildPartInterfaceLogRequest();
    }

    /**
     * 根据当前的request信息来设置初始信息
     */
    private void buildPartInterfaceLogRequest() {
        // 获取当前请求的基础信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (request != null) {
            // 请求url
            knifeInterfaceLogRecord.setInterfaceUrl(request.getRequestURL() != null ? request.getRequestURL().toString() : null);
            // 请求方法
            knifeInterfaceLogRecord.setInterfaceRequestMethod(request.getMethod());
            // IP
            String remoteAddr = request.getRemoteAddr();
            // user_agent
            knifeInterfaceLogRecord.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));

            knifeInterfaceLogRecord.setReferer(request.getHeader(HttpHeaders.REFERER));

            knifeInterfaceLogRecord.setIp(remoteAddr);
        }
    }


    @Override
    public void buildInterfaceLogResponse(Object responseObj, Class<?>... ignoreClassArray) {
        super.buildInterfaceLogResponse(responseObj, ignoreClassArray);
        // 更新状态
        knifeInterfaceLogRecord.setInterfaceResponseStatus(httpStatus.getReasonPhrase());
        knifeInterfaceLogRecord.setInterfaceResponseCode(String.valueOf(httpStatus.value()));
    }

    @Override
    public void buildInterfaceLogRequest(Object[] args, Class<?>... ignoreClassArray) {
        // 不会使用无需处理
    }

    @Override
    public void buildInterfaceLogRequest(Map<String, Object> map, Class<?>... ignoreClassArray) {
        // 如果ignoreClassList不为空,需要过滤请求的args
        Object requestBodyObj = map.get(requestBodyObjName);
        if (requestBodyObj != null) {
            // 记录响应结果
            super.setRequestBodyContentOrFileId(requestBodyObj);
            map.remove(requestBodyObjName);
        }
        // controller中参数只能有一个RequestBody参数，其他都是路径参数
        if (!CollectionUtils.isEmpty(map)) {
            super.setRequestUrlContentOrFileId(map);
        }
    }
}
